#include "connectPool.h"
#include <fstream>
#include <iostream>
#include <jsoncpp/json/json.h>

using namespace std;

/* 提供一个静态方法，让外部调用唯一实例 */
ConnectPool *ConnectPool::getConnectPool()
{
    static ConnectPool pool;        // 静态局部变量，只能通过该函数进行访问，但是生命周期到程序结束才会析构
                                    // 连接池只会被创建一次
    return &pool;
}

/* 析构函数 */
ConnectPool::~ConnectPool()
{

}

/* 从连接池中取出一个Mysql连接 */
std::shared_ptr<mysqlDataBase> ConnectPool::getConnection()
{
    /* 得到的MysqlDataBase对象一定是已经连接上服务器的 */
    /*
        1.判断连接池中是否有可用的连接
        2.如果没有，就条件等待
    */

    /* 加锁 */
    unique_lock<mutex> locker(m_mutex);
    while(m_connectQueue.empty())
    {
        /* 等待条件变量 */
        if(cv_status::timeout == m_cond_consume.wait_for(locker, chrono::milliseconds(500)))
        {
            /*继续等待 */
            if(m_connectQueue.empty())
            {
                continue;
            }
        }
    }
    /* 程序到这个地方：数据库连接池有可用的连接 */
    shared_ptr<mysqlDataBase> connPtr(m_connectQueue.front(), [this](mysqlDataBase * conn){
        
        /* 共享资源 */
        m_mutex.lock();
        /* 刷新时间 */
        conn->refreshAliveTime();
        /* 添加到连接池里面 */
        m_connectQueue.push(conn);
        /* 解锁 */
        m_mutex.unlock();
    });

    /* 取出队头元素 */
    m_connectQueue.pop();

    /* 唤醒生产者 */
    m_cond_produce.notify_all();

    /* 得到的MysqlDataBase对象一定是已经连接上服务器，*/
    return connPtr;
}

/* 解析配置文件 */
bool ConnectPool::parseConfigFile(const string &path)
{
    ifstream ifs(path);
    if(ifs.is_open() == false)
    {
        cout << "failed to open config file." << endl;
        return false;
    }
    Json::Reader rd;
    Json::Value root;

    if(rd.parse(ifs, root) == false)
    {
        cout << "json parse failed." << endl;
        return false;
    }

    m_hostname = root["hostname"].asString();
    m_username = root["username"].asString();
    m_passwd = root["passwd"].asString();
    m_port = root["port"].asInt();
    m_dbname = root["dbname"].asString();
    m_minSize = root["minSize"].asInt();
    m_maxSize = root["maxSize"].asInt();
    m_maxIdleTime = root["maxIdleTime"].asInt();

    return true;
}

/* 添加连接到连接池中 */
bool ConnectPool::addConnection()
{
    mysqlDataBase * conn = new mysqlDataBase(m_hostname, m_username, m_passwd);

    /* todo.... */
    /* 对每一个连接上数据库的连接打上时间戳 */
    conn->refreshAliveTime();

    /* 连接数据库 */
    conn->connectDB(m_dbname);

    /* 添加连接到连接池*/
    m_connectQueue.push(conn);

    return true;

}
/* 生产线程入口 - 向数据库连接池生产连接句柄 */
void ConnectPool::produceConnection()
{
    while(true)
    {
        /* unique_lock智能锁 */
        unique_lock<mutex> locker(m_mutex);
        while(m_connectQueue.size() >= m_minSize)
        {
            /* 等待条件变量 */
            m_cond_produce.wait(locker);
        }
        /* 添加连接到连接池 */
        addConnection();

        /* 通知消费者 */
        m_cond_consume.notify_all();
    }
}

/* 回收线程 - 回收数据库连接池的连接 */
void ConnectPool::recycleConnection()
{
    while(true)
    {
        /* 休眠1s */
        this_thread::sleep_for(chrono::seconds(1));

        /* 共享资源加锁 */
        unique_lock<mutex> locker(m_mutex);

        /* 连接池的连接句柄必须大于最小值 */
        while(m_connectQueue.size() > m_minSize)
        {
            /* 查看队头 */
            mysqlDataBase * conn = m_connectQueue.front();

            /* 存活时间 = 当前时间 - 上次操作时间 */
            if(conn->getAliveTime() > m_maxIdleTime)
            {
                m_connectQueue.pop();
                delete conn;
                conn = nullptr;
            }
        }
    }
    
}

/* 构造函数私有化 */
ConnectPool::ConnectPool()
{
    /* 解析json配置文件 */
    if(parseConfigFile("./config.json") == false)
    {
        return;
    }

    /* 将连接添加到连接池中 */
    for(int idx = 0; idx < m_minSize; idx++)
    {
        addConnection();
    }

    /* 生产者 */
    thread produce(&ConnectPool::produceConnection, this);

    /* 回收者 */
    thread recycle(&ConnectPool::recycleConnection, this);

    /* 线程分离 */
    produce.detach();
    recycle.detach();
}

